package se.jocke.nb.log.ui;

import java.awt.Dimension;
import java.io.File;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Box;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.JFileChooser;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.util.Lookup;
import org.openide.windows.TopComponent;
import org.openide.util.NbBundle.Messages;
import se.jocke.nb.log.HistoryManager;
import se.jocke.nb.log.LogReader;
import se.jocke.nb.log.LogViewFilterManager;
import se.jocke.nb.log.internal.LogReaderImpl;
import se.jocke.nb.log.model.LogViewFilter;

/**
 * Top component which displays something.
 */
@ConvertAsProperties(
        dtd = "-//se.jocke.nb.log.ui//LogView//EN",
        autostore = false
)
@TopComponent.Description(
        preferredID = "LogViewTopComponent",
        //iconBase="SET/PATH/TO/ICON/HERE", 
        persistenceType = TopComponent.PERSISTENCE_ALWAYS
)
@TopComponent.Registration(mode = "editor", openAtStartup = false)
@ActionID(category = "Window", id = "se.jocke.nb.log.ui.LogViewTopComponent")
@ActionReference(path = "Menu/Window" /*, position = 333 */)
@TopComponent.OpenActionRegistration(
        displayName = "#CTL_LogViewAction",
        preferredID = "LogViewTopComponent"
)
@Messages({
    "CTL_LogViewAction=LogView",
    "CTL_LogViewTopComponent=LogView Window",
    "HINT_LogViewTopComponent=This is a LogView window"
})
public final class LogViewTopComponent extends TopComponent {

    private static final Logger LOG = Logger.getLogger(LogViewTopComponent.class.getName());

    private final TailAdjustmentListener adjustmentListener;

    private LogReader logReader;

    private final DefaultListModel<LineNumber> lineNumberListModel;

    public LogViewTopComponent() {
        lineNumberListModel = new DefaultListModel<LineNumber>();
        initComponents();
        setName(Bundle.CTL_LogViewTopComponent());
        setToolTipText(Bundle.HINT_LogViewTopComponent());
        adjustmentListener = new TailAdjustmentListener();
        logTextScrollPane.getVerticalScrollBar().addAdjustmentListener(adjustmentListener);
        searchTextResultScrollPane.setRowHeaderView(searchScrollPane);
        new SyncAdjustmentListener(searchScrollPane, searchTextResultScrollPane).register();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        fileChooser = new javax.swing.JFileChooser();
        searchScrollPane = new javax.swing.JScrollPane();
        searchLineNumberList = new javax.swing.JList();
        jToolBar1 = new javax.swing.JToolBar();

        fileButton = new javax.swing.JButton();
        historyComboBox = new javax.swing.JComboBox();
        toggleTailButton = new javax.swing.JButton();
        jSeparator1 = new javax.swing.JToolBar.Separator();
        filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(50, 3), new java.awt.Dimension(200, 3), new java.awt.Dimension(50, 3));
        jSplitPane1 = new javax.swing.JSplitPane();
        logTextScrollPane = new javax.swing.JScrollPane();
        logTextPane = new javax.swing.JTextPane();
        jPanel1 = new javax.swing.JPanel();
        searchButton = new javax.swing.JButton();
        searchTextField = new javax.swing.JTextField();
        jCheckBox1 = new javax.swing.JCheckBox();
        searchTextResultScrollPane = new javax.swing.JScrollPane();
        searchTextPane = new javax.swing.JTextPane();
        jCheckBox2 = new javax.swing.JCheckBox();
        jSpinner1 = new javax.swing.JSpinner();

        searchScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        searchScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
        searchScrollPane.setPreferredSize(new java.awt.Dimension(40, 130));

        searchLineNumberList.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 0, 0, 0));
        searchLineNumberList.setModel(lineNumberListModel);
        searchLineNumberList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        searchLineNumberList.setCellRenderer(new LineNumberListCellRenderer(searchTextPane));
        searchScrollPane.setViewportView(searchLineNumberList);

        jToolBar1.setRollover(true);

        org.openide.awt.Mnemonics.setLocalizedText(fileButton, org.openide.util.NbBundle.getMessage(LogViewTopComponent.class, "LogViewTopComponent.fileButton.text")); // NOI18N
        fileButton.setFocusable(false);
        fileButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        fileButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        fileButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                fileButtonActionPerformed(evt);
            }
        });
        jToolBar1.add(fileButton);

        historyComboBox.setPreferredSize(new java.awt.Dimension(500, 27));
        historyComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                historyComboBoxActionPerformed(evt);
            }
        });
        jToolBar1.add(historyComboBox);

        org.openide.awt.Mnemonics.setLocalizedText(toggleTailButton, org.openide.util.NbBundle.getMessage(LogViewTopComponent.class, "LogViewTopComponent.toggleTailButton.text")); // NOI18N
        toggleTailButton.setFocusable(false);
        toggleTailButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        toggleTailButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        toggleTailButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                toggleTailButtonActionPerformed(evt);
            }
        });
        jToolBar1.add(toggleTailButton);
        jToolBar1.add(jSeparator1);
        jToolBar1.add(Box.createHorizontalGlue());
        jToolBar1.add(filler1);

        jSplitPane1.setDividerLocation(400);
        jSplitPane1.setDividerSize(15);
        jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        logTextPane.setEditable(false);
        logTextPane.setPreferredSize(new java.awt.Dimension(6, 300));
        logTextScrollPane.setViewportView(logTextPane);

        jSplitPane1.setTopComponent(logTextScrollPane);

        org.openide.awt.Mnemonics.setLocalizedText(searchButton, org.openide.util.NbBundle.getMessage(LogViewTopComponent.class, "LogViewTopComponent.searchButton.text")); // NOI18N
        searchButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                searchButtonActionPerformed(evt);
            }
        });

        searchTextField.setText(org.openide.util.NbBundle.getMessage(LogViewTopComponent.class, "LogViewTopComponent.searchTextField.text")); // NOI18N
        searchTextField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                searchTextFieldActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jCheckBox1, org.openide.util.NbBundle.getMessage(LogViewTopComponent.class, "LogViewTopComponent.jCheckBox1.text")); // NOI18N
        jCheckBox1.setFocusable(false);
        jCheckBox1.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);

        searchTextPane.setEditable(false);
        searchTextResultScrollPane.setViewportView(searchTextPane);

        org.openide.awt.Mnemonics.setLocalizedText(jCheckBox2, org.openide.util.NbBundle.getMessage(LogViewTopComponent.class, "LogViewTopComponent.jCheckBox2.text")); // NOI18N

        jSpinner1.setModel(new javax.swing.SpinnerNumberModel(100, 1, 9999, 100));

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addComponent(searchButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 633, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, 76, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jCheckBox2)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jCheckBox1)
                .addGap(6, 6, 6))
            .addComponent(searchTextResultScrollPane, javax.swing.GroupLayout.Alignment.TRAILING)
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(searchButton)
                    .addComponent(searchTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jCheckBox1)
                    .addComponent(jCheckBox2)
                    .addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(searchTextResultScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 228, Short.MAX_VALUE))
        );

        jSplitPane1.setRightComponent(jPanel1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addGap(12, 12, 12)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jSplitPane1)
                    .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 297, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents

    private void fileButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileButtonActionPerformed
        int answer = fileChooser.showOpenDialog(this);
        if (answer == JFileChooser.APPROVE_OPTION) {
            openLogFile(fileChooser.getSelectedFile());
        }

    }//GEN-LAST:event_fileButtonActionPerformed

    private void openLogFile(File file) {
        LOG.log(Level.INFO, "Will open File {0}", file.getName());
        if (logReader != null) {
            logReader.close();
        }
        logTextPane.setText("");
        logReader = new LogReaderImpl(file);
        logReader.tail(new TextPaneTailListener(logTextPane, Lookup.getDefault().lookup(LogViewFilterManager.class).findAll()));
        HistoryManager historyManager = Lookup.getDefault().lookup(HistoryManager.class);
        historyManager.addToHistory(file);
        historyComboBox.setModel(new DefaultComboBoxModel(historyManager.getHistory().toArray()));
    }

    private void toggleTailButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_toggleTailButtonActionPerformed
        adjustmentListener.toggle();
    }//GEN-LAST:event_toggleTailButtonActionPerformed

    private void historyComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_historyComboBoxActionPerformed
        openLogFile((File) historyComboBox.getSelectedItem());
    }//GEN-LAST:event_historyComboBoxActionPerformed

    private void searchButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchButtonActionPerformed
        final File current = (File) historyComboBox.getSelectedItem();

        if (searchTextField.getText() == null) {
            return;
        }

        if (logReader != null) {
            logReader.close();
        }

        if (current != null) {
            lineNumberListModel.clear();
            searchTextPane.setText("");
            logReader = new LogReaderImpl(current);
            List<LogViewFilter> filters = Lookup.getDefault().lookup(LogViewFilterManager.class).findAll();
            TextPaneLineListener lineListener = new TextPaneLineListener(Matchers.contains(searchTextField.getText()), searchTextPane, lineNumberListModel, filters);
            logReader.seek(true, 100, lineListener);
        }

    }//GEN-LAST:event_searchButtonActionPerformed

    private void searchTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchTextFieldActionPerformed
        searchButtonActionPerformed(evt);
    }//GEN-LAST:event_searchTextFieldActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton fileButton;
    private javax.swing.JFileChooser fileChooser;
    private javax.swing.Box.Filler filler1;
    private javax.swing.JComboBox historyComboBox;
    private javax.swing.JCheckBox jCheckBox1;
    private javax.swing.JCheckBox jCheckBox2;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JToolBar.Separator jSeparator1;
    private javax.swing.JSpinner jSpinner1;
    private javax.swing.JSplitPane jSplitPane1;
    private javax.swing.JToolBar jToolBar1;
    private javax.swing.JTextPane logTextPane;
    private javax.swing.JScrollPane logTextScrollPane;
    private javax.swing.JButton searchButton;
    private javax.swing.JList searchLineNumberList;
    private javax.swing.JScrollPane searchScrollPane;
    private javax.swing.JTextField searchTextField;
    private javax.swing.JTextPane searchTextPane;
    private javax.swing.JScrollPane searchTextResultScrollPane;
    private javax.swing.JButton toggleTailButton;
    // End of variables declaration//GEN-END:variables
    @Override
    public void componentOpened() {
        historyComboBox.setModel(new DefaultComboBoxModel(Lookup.getDefault().lookup(HistoryManager.class).getHistory().toArray()));
    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
    }

    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }
}
